package pl.kosmowska.ewelina.client.app.activity;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import pl.kosmowska.ewelina.client.Constants;
import pl.kosmowska.ewelina.client.ViewMessages;
import pl.kosmowska.ewelina.client.app.ClientFactory;
import pl.kosmowska.ewelina.client.app.activity.places.IncomePlace;
import pl.kosmowska.ewelina.client.app.activity.places.OutcomeEditPlace;
import pl.kosmowska.ewelina.client.app.activity.places.OutcomePlace;
import pl.kosmowska.ewelina.client.app.activity.places.SavingsEditPlace;
import pl.kosmowska.ewelina.client.app.activity.places.SignInPlace;
import pl.kosmowska.ewelina.client.app.requestfactory.OutcomeDataRequest;
import pl.kosmowska.ewelina.client.app.requestfactory.dto.CategoryDataProxy;
import pl.kosmowska.ewelina.client.app.requestfactory.dto.OutcomeDataProxy;
import pl.kosmowska.ewelina.client.app.requestfactory.dto.SavingsDataProxy;
import pl.kosmowska.ewelina.client.app.user.AuthEvent;
import pl.kosmowska.ewelina.client.app.user.AuthEvent.Auth;
import pl.kosmowska.ewelina.client.app.user.AuthEventHandler;
import pl.kosmowska.ewelina.client.views.OutcomeEditView;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.web.bindery.requestfactory.shared.EntityProxyId;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.ServerFailure;

public class OutcomeEditActivity extends AbstractActivity implements OutcomeEditView.Presenter {

	private OutcomeEditView view;

	private ClientFactory clientFactory;

	private boolean isOutcome;

	private OutcomeDataProxy outcomeData;

	private EntityProxyId<OutcomeDataProxy> outcomeDataId;

	public OutcomeEditActivity(OutcomeEditPlace place, ClientFactory clientFactory) {
		this.outcomeData = place.getOutcomeData();
		this.outcomeDataId = place.getOutcomeDataId();
		this.clientFactory = clientFactory;
		if(outcomeData==null){
			this.isOutcome=place.isOutcome();
		}else{
			this.isOutcome = outcomeData.getValue()<0 ? true : false;
		}
	}


	@Override
	public void start(AcceptsOneWidget containerWidget, EventBus eventBus) {
		view = clientFactory.getOutcomeEditView();
		view.setPresenter(this);
		view.setOutcome(isOutcome);
		initAuthorisationForView();
		if (outcomeDataId != null) { 
			getOutcomeData();
		} else {
			view.setData(outcomeData);
			view.setOutcome(isOutcome);
			startView();
		}
		containerWidget.setWidget(view.asWidget());

	}


	private void startView() {
		if (clientFactory.getIsLoggedIn() == null) {
			// wait for login event b/c hasn't happened yet
		} else if (clientFactory.getIsLoggedIn() == true) {
			view.reInitView();
		} else if (clientFactory.getIsLoggedIn() == false) { 
			goTo(new SignInPlace());
		}	
	}
	
	private void initAuthorisationForView() {
		if (view.isAlreadyInit() == false) {
			clientFactory.getEventBus().addHandler(AuthEvent.TYPE, new AuthEventHandler() {
				public void onAuthEvent(AuthEvent event) {
					Auth e = event.getAuthEvent();
					if (e == Auth.LOGGEDIN) {
						view.reInitView();
					} else if (e == Auth.LOGGEDOUT) {
						goTo(new SignInPlace());
					}
				}
			});
		}
		view.setAlreadyInit(true);
	}
	
	private void getOutcomeData() {
		Request<OutcomeDataProxy> request = clientFactory.getRequestFactory().getOutcomeDataRequest().find(outcomeDataId);
		request.fire(new Receiver<OutcomeDataProxy>() {
			public void onSuccess(OutcomeDataProxy outcomeData) {
				setData(outcomeData);
				view.setData(outcomeData);
				if(outcomeData.getValue()>0)
					view.setOutcome(false);
				else 
					view.setOutcome(true);
				startView();
			}

			public void onFailure(ServerFailure error) {
				super.onFailure(error);
			}
		});


	}

	private void setData(OutcomeDataProxy outcomeData) {
		this.outcomeData = outcomeData;
		if(outcomeData.getValue()>0)
			isOutcome = false;
		else 
			isOutcome = true;

	}


	public void goTo(Place place) {
		clientFactory.getPlaceController().goTo(place);
	}

	@Override
	public void loadCategories() {
		clientFactory.getAppLoadingView().startProcessing();
		Request<List<CategoryDataProxy>> req = clientFactory.getRequestFactory().getCategoryDataRequest().findCategoryDataByUser().with("items");
		req.fire(new Receiver<List<CategoryDataProxy>>() {
			public void onSuccess(List<CategoryDataProxy> categoryDataList) {
				clientFactory.getAppLoadingView().stopProcessing();
				view.initCategoryList(categoryDataList);
			}
			public void onFailure(ServerFailure error) {
				clientFactory.getAppLoadingView().stopProcessing();
				super.onFailure(error);
			}
		});

	}

	private OutcomeDataProxy prepareOutcomeDataToSave(String name, float value, String category, Date date, String priority, OutcomeDataRequest request, String sponsorKey ){
		// is it create or edit
		OutcomeDataProxy data = null;
		if (outcomeData == null) { // insert|create
			data = request.create(OutcomeDataProxy.class);

		} else { // update|edit
			data = request.edit(outcomeData);
			Log.error(data.getId());
		}

		// persist these
		data.setName(name);
		data.setValue(value);
		data.setCategory(category);
		data.setDate(date);
		data.setPriority(priority);
		data.setMultiple(false);
		data.setSponsorKey(sponsorKey);
		return data;
	}
	public void saveSingleOutcomeData(String name, float value, String category, Date date, String priority, String sponsorKey) {
	
		OutcomeDataRequest request = clientFactory.getRequestFactory().getOutcomeDataRequest();
		OutcomeDataProxy outcomedataToSave = prepareOutcomeDataToSave(name,value, category,date,priority, request, null);

		request.persist(outcomedataToSave).fire(new Receiver<OutcomeDataProxy>() {
			public void onSuccess(OutcomeDataProxy outcomeData) {
				if(outcomeData.getValue()<0)
					clientFactory.getPlaceController().goTo(new OutcomePlace());
				else
					clientFactory.getPlaceController().goTo(new IncomePlace());
			}
			public void onFailure(ServerFailure error) {
				Log.error(error.getMessage());
				view.showMessage(ViewMessages.ERROR_ON_PERSIST);
				super.onFailure(error);
			}
		});    		    	
	}
	
	public void generateSavingsPlan(String name, float value,
			String category, Date date, String priority) {
		OutcomeDataRequest req = clientFactory.getRequestFactory().getOutcomeDataRequest();
		OutcomeDataProxy outcomeDataProxy= req.create(OutcomeDataProxy.class);
		outcomeDataProxy.setName(name);
		outcomeDataProxy.setValue(value);
		outcomeDataProxy.setDate(date);
		outcomeDataProxy.setPriority(priority);
		outcomeDataProxy.setCategory(category);
		outcomeDataProxy.setMultiple(false);
		SavingsEditPlace place = new SavingsEditPlace(outcomeDataProxy,req);
		clientFactory.getPlaceController().goTo(place);
		
		
	}

	public void generateLoan(String name, float value, String category,
			Date date, String priority) {
		
		
	}
	
	public void reInitSavingsPlan(final float newValue) {
		Request<SavingsDataProxy> req = clientFactory.getRequestFactory().getSavingsDataRequest().findSavingsData(outcomeData.getGroupKey());
		req.fire(new Receiver<SavingsDataProxy>() {
			public void onSuccess(SavingsDataProxy returnedSavingsData) {
				if(returnedSavingsData!=null)
					getSavingsOutcomeList(returnedSavingsData,newValue);
			}
			public void onFailure(ServerFailure error) {
				super.onFailure(error);
			}
		});

	}

	private void getSavingsOutcomeList(final SavingsDataProxy savingsData, final float newValue) {
		Request<List<OutcomeDataProxy>> req = clientFactory.getRequestFactory().getOutcomeDataRequest().getSavingsOutcomeList(outcomeData.getGroupKey());
		req.fire(new Receiver<List<OutcomeDataProxy>>() {
			public void onSuccess(List<OutcomeDataProxy> outcomeDataList) {
				if (outcomeDataList!=null)
					alterSavingsPlan(savingsData, outcomeDataList,newValue);
			}
			public void onFailure(ServerFailure error) {
				super.onFailure(error);
			}
		});

	}
	
	private void alterSavingsPlan(SavingsDataProxy savingsData,
			List<OutcomeDataProxy> savingsOutcomeList, final float newValue) {

		List<OutcomeDataProxy> previousOutcomeList= new ArrayList<OutcomeDataProxy>();
		final List<OutcomeDataProxy> followingOutcomeList= new ArrayList<OutcomeDataProxy>();

		for(OutcomeDataProxy outcome:savingsOutcomeList){
			if(outcome.getDate().getTime()<outcomeData.getDate().getTime()){
				previousOutcomeList.add(outcome);
			}else if(outcome.getDate().getTime()>outcomeData.getDate().getTime()){
				followingOutcomeList.add(outcome);
			}
		}

		int periodCount=followingOutcomeList.size();

		if (periodCount==0){
			view.showMessage(ViewMessages.ERROR_ON_DELETE);
			return;
		}

		float value =findValueToPay(savingsData,previousOutcomeList, newValue);
		float installmentValue=round(value/(float)periodCount);

		OutcomeDataRequest request= clientFactory.getRequestFactory().getOutcomeDataRequest();
		ArrayList <OutcomeDataProxy> outcomeDataListToSave=prepareOutcomeListForSavingsPlan(followingOutcomeList, installmentValue, savingsData, request);

		if(!outcomeDataListToSave.isEmpty()) {
			request.persistAllSavings(outcomeDataListToSave).fire(new Receiver<List<OutcomeDataProxy>>() {
				public void onSuccess(List<OutcomeDataProxy> returnedOutcomeData) {
					float value = -abs(newValue);
					saveSingleOutcomeData(outcomeData.getName(), value, outcomeData.getCategory(), outcomeData.getDate(), outcomeData.getPriority(), outcomeData.getSponsorKey()); 

				}
				public void onFailure(ServerFailure error) {
					view.showMessage(ViewMessages.ERROR_ON_DELETE);
					super.onFailure(error);
				}
			});    

		}else {
			view.showMessage(ViewMessages.ERROR_ON_DELETE);
		}
	}
	
	private float abs(float value) {
		if (value>0)
			return value;
		else
			return (-value);
	}
	
	private ArrayList<OutcomeDataProxy> prepareOutcomeListForSavingsPlan(
			List<OutcomeDataProxy> followingOutcomeList, float value,
			SavingsDataProxy savingsData, OutcomeDataRequest request) {

		ArrayList<OutcomeDataProxy> outcomeListToEdit= new ArrayList<OutcomeDataProxy>();
		for(OutcomeDataProxy outcomeData:followingOutcomeList){
			OutcomeDataProxy data =  request.edit(outcomeData);
			data.setCategory(Constants.SAVINGS_CATEGORY);
			data.setDate(outcomeData.getDate());
			data.setMultiple(true);
			data.setName(savingsData.getName());
			//TODO: do zmniany
			data.setPriority("Wysoki");
			data.setRepeatEndDate(savingsData.getEndDate());
			//TODO: do zmniany
			data.setRepeatInterval(3);
			data.setValue(value);
			data.setGroupKey(outcomeData.getGroupKey());
			outcomeListToEdit.add(data);

		}

		return outcomeListToEdit;

	}
	
	
	private float findValueToPay(SavingsDataProxy savingsData,
			List<OutcomeDataProxy> outcomeDataList2, float newValue) {
		float valuePaid=0;
		for(OutcomeDataProxy outcomeData:outcomeDataList2){
			valuePaid=outcomeData.getValue();
		}
		float value = newValue;
		value=-abs(value);
		return (-savingsData.getValue()-valuePaid-value);
	}
	
	float round (float number){
		NumberFormat newFormat =NumberFormat.getFormat("#.##");
		return  Float.valueOf(newFormat.format(number));
	}
	
	public void editAllRepeatedOutcomeData(final String name, final float value, final String category, Date date, final String priority, final Date repeatEndDate, final long interval, final int repeatFrequencyBoxIndex) {
		clientFactory.getRequestFactory().getOutcomeDataRequest().findOutcomeData(outcomeData.getGroupKey()).fire(new Receiver<OutcomeDataProxy>() {
			public void onSuccess(OutcomeDataProxy returnedOutcomeData) {
				final OutcomeDataRequest request = clientFactory.getRequestFactory().getOutcomeDataRequest();
				List <OutcomeDataProxy>  outcomeDataListToSave = prepareRepeatedOutcomeDataToSave( name,  value,  category,  returnedOutcomeData.getDate(),  priority,  repeatEndDate,  interval,   repeatFrequencyBoxIndex, request);
				if(!outcomeDataListToSave.isEmpty()) {
					request.editRepeatedOutcomeData(outcomeData.getGroupKey(),outcomeDataListToSave).fire(new Receiver<List<OutcomeDataProxy>>() {
						public void onSuccess(List<OutcomeDataProxy> returnedOutcomeData) {
							if(isOutcome)
								clientFactory.getPlaceController().goTo(new OutcomePlace());
							else
								clientFactory.getPlaceController().goTo(new IncomePlace());
						}
						public void onFailure(ServerFailure error) {
							view.showMessage(ViewMessages.ERROR_ON_PERSIST);
							super.onFailure(error);
						}
					});    

				}else {
					view.showMessage(ViewMessages.ERROR_ON_PERSIST);
				}
			}
			public void onFailure(ServerFailure error) {
				view.showMessage("B\u0142\u0105d zapisu. Spr\u00F3buj ponownie!");
				super.onFailure(error);
			}
		}); 

	}
	
	private List<OutcomeDataProxy> prepareRepeatedOutcomeDataToSave(String name, float value, String category, Date date, String priority, Date repeatEndDate, long interval,  int repeatFrequencyBoxIndex,  OutcomeDataRequest request){

		List <OutcomeDataProxy> outcomeDataList = new ArrayList <OutcomeDataProxy> () ;

		//TODO: tutaj ewentualne b�edy
		while (date.getTime()<=repeatEndDate.getTime()){
			OutcomeDataProxy outcomeData = request.create(OutcomeDataProxy.class);
			outcomeData.setName(name);
			outcomeData.setValue(value);
			outcomeData.setCategory(category);
			outcomeData.setDate(new Date(date.getTime()));
			outcomeData.setPriority(priority);
			outcomeData.setMultiple(true);
			outcomeData.setRepeatEndDate(repeatEndDate);
			outcomeData.setRepeatInterval(repeatFrequencyBoxIndex);
			outcomeDataList.add(outcomeData);
			if(interval==0){
				if (date.getMonth()==11)
					date.setYear(date.getYear()+1);	
				date.setMonth((date.getMonth()+1)%12);
			}else if(interval==1){
				if (date.getMonth()>7)
					date.setYear(date.getYear()+1);	
				date.setMonth((date.getMonth()+3)%12);
			}else{
				date= new Date(date.getTime()+interval);
			}
		}

		return outcomeDataList;
	}

	public void editFollowingRepeatedOutcomeData(String name, float value, String category, Date date, String priority, Date repeatEndDate, long interval, int repeatFrequencyBoxIndex) {

		// get the requestContext
		OutcomeDataRequest request = clientFactory.getRequestFactory().getOutcomeDataRequest();


		List <OutcomeDataProxy>  outcomeDataListToSave = prepareRepeatedOutcomeDataToSave( name,  value,  category,  date,  priority,  repeatEndDate,  interval,   repeatFrequencyBoxIndex, request);

		Receiver<List<OutcomeDataProxy>> receiver = new Receiver<List<OutcomeDataProxy>>() {
			public void onSuccess(List<OutcomeDataProxy> returnedOutcomeData) {
				if(isOutcome)
					clientFactory.getPlaceController().goTo(new OutcomePlace());
				else
					clientFactory.getPlaceController().goTo(new IncomePlace());
			}
			public void onFailure(ServerFailure error) {
				view.showMessage(ViewMessages.ERROR_ON_PERSIST);
				super.onFailure(error);
			}
		};
		if(!outcomeDataListToSave.isEmpty()) {
			request.editRepeatedOutcomeData(outcomeData.getGroupKey(),outcomeDataListToSave, outcomeData.getDate()).fire(receiver);  
		}else {
			view.showMessage(ViewMessages.ERROR_ON_PERSIST);
		}
	}
	
	public void saveRepeatedOutcomeData(String name, float value, String category, Date date, String priority, Date repeatEndDate, long interval, int repeatFrequencyBoxIndex) {

		// get the requestContext
		OutcomeDataRequest request = clientFactory.getRequestFactory().getOutcomeDataRequest();

		List <OutcomeDataProxy>  outcomeDataListToSave = prepareRepeatedOutcomeDataToSave( name,  value,  category,  date,  priority,  repeatEndDate,  interval,   repeatFrequencyBoxIndex, request);

		if(!outcomeDataListToSave.isEmpty()) {
			request.persistAllRepeatedOutcome(outcomeDataListToSave).fire(new Receiver<List<OutcomeDataProxy>>() {
				public void onSuccess(List<OutcomeDataProxy> returnedOutcomeData) {
					if(isOutcome)
						clientFactory.getPlaceController().goTo(new OutcomePlace());
					else
						clientFactory.getPlaceController().goTo(new IncomePlace());
				}
				public void onFailure(ServerFailure error) {
					view.showMessage(ViewMessages.ERROR_ON_PERSIST);
					super.onFailure(error);
				}
			});    

		}else {
			view.showMessage(ViewMessages.ERROR_ON_PERSIST);
		}
	}
	
	public void saveIfBalancePositive(final String name, final float value, final String category,final Date date,final String priority)  {
		Request<Float> request =clientFactory.getRequestFactory().getOutcomeDataRequest().getSum(date);
		request.fire(new Receiver<Float>() {
			public void onSuccess(Float sum) {
				if ((sum+value)>0)
					saveSingleOutcomeData(name, value, category, date, priority,null);
				else
					view.initSavingsOptionDialog();
			}
			public void onFailure(ServerFailure error) {
				super.onFailure(error);
			}
		});
	}
	
}
